import info.gridworld.actor.Actor;
import info.gridworld.grid.BoundedGrid;
import info.gridworld.grid.Grid;
import info.gridworld.grid.Location;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

import javax.swing.JOptionPane;


public class Combat extends Map
{
    private ArrayList<Character> turnOrder = new ArrayList<Character>();

    private int enemyCount;

    private PlayerCharacter player;

    private int count = 0;

    private int level;

    private int[] num = { 1, 2, 3, 4 };

    private ArrayList<Character> enemiesToFight;

    private ArrayList<Character> order;

    private Boolean inputAllowed;

    private Boolean moveAllowed;

    private Boolean attackOn;

    private Boolean skillOn;


    public Combat( int lvl )
    {
        super( new BoundedGrid( 10, 10 ) );
        level = lvl;
    }


    public void initializeCombat()
    {
        enemyCount = (int)( 1 + Math.random() * 3 );
        player = new PlayerCharacter( level );
        enemiesToFight = new ArrayList<Character>();
        for ( int i = 0; i < enemyCount; i++ )
        {
            int enemy = 0;
            if ( level == 1 )
            {
                enemy = (int)( 1 + Math.random() );
            }
            else if ( level == 2 )
            {
                enemy = (int)( 1 + Math.random() * 2 );
            }
            else if ( level == 3 )
            {
                enemy = (int)( 2 + Math.random() * 2 );
            }

            if ( enemy == 1 )
            {
                enemiesToFight.add( new Slime( level ) );
            }
            else if ( enemy == 2 )
            {
                enemiesToFight.add( new Skeleton( level ) );
            }
            else if ( enemy == 3 )
            {
                enemiesToFight.add( new Orc( level ) );
            }
        }
        int r = 0;
        int c = this.getGrid().getNumCols() - 1;
        for ( int e = 0; e < enemiesToFight.size(); e++ )
        {
            Location loc = new Location( r, c );
            enemiesToFight.get( e ).putSelfInGrid( this.getGrid(), loc );
            c -= 2;
            if ( c < 0 )
            {
                r++;
                c = this.getGrid().getNumCols() - 1;
            }
        }
        player.putSelfInGrid( this.getGrid(), new Location( this.getGrid()
            .getNumRows() - 1, 0 ) );
        this.setMessage( "" );

    }


    public void victory()
    {
        JOptionPane.showMessageDialog( null,
            "It appears you are still alive... How unfortunate" );
        getFrame().setVisible( false );
        getFrame().dispose();
    }


    public void attack( Character attacker, Character defender )
    {
        int damage = 0;
        if ( attacker.getATK().getDamage() != -1 )
        {
            damage = attacker.getATK().getDamage()
                + (int)( Math.random() * ( level + 1 ) + 1 )
                - defender.getDEF();
            defender.changeHP( defender.currentHP() - damage );
        }

        if ( attacker.getATK().getDamage() == -1
            && !attacker.getATK().getSkillName().equals( "" ) )
        {
            this.addMessage( attacker.getName() + " uses "
                + attacker.getATK().getSkillName() + "." );

            this.addMessage( attacker.getName() + " now has "
                + attacker.currentMP() + " MP" + '\n' );
        }
        else if ( damage > 0 )
        {
            this.addMessage( attacker.getName() + " uses "
                + attacker.getATK().getSkillName() + " and hits for " + damage
                + " damage." );
            if ( !attacker.getATK().getSkillName().equals( "Attack" ) )
            {
                this.addMessage( attacker.getName() + " now has "
                    + attacker.currentMP() + " MP" );
            }
            this.addMessage( defender.getName() + " is now at "
                + defender.currentHP() + " HP" + '\n' );

        }

        if ( defender.currentHP() <= 0 )
        {
            if ( defender instanceof Mobs )
            {
                enemyCount--;
                defender.removeSelfFromGrid();
                order.remove( defender );
                if ( enemyCount == 0 )
                {
                    victory();
                }
            }
            else
            {
                JOptionPane.showMessageDialog( null, "You have died. GG" );
                System.exit( 0 );
            }
        }
    }


    public void takeStep()
    {
        this.inputAllowed = false;

        if ( count >= order.size() )
        {
            count = 0;
        }
        Character c = order.get( count );
        if ( c instanceof Mobs )
        {
            c.act();
            attack( c, ( (Mobs)c ).getNearestEnemy() );
            count++;
            takeStep();
        }
        else
        {
            playerAct();
        }

        // if ( enemyCount != 0 )

    }


    public void run()
    {
        initializeCombat();
        order = enemiesToFight;
        order.add( player );
        takeStep();
    }


    public void playerAct()
    {
        if ( player.getDEF() == 100 )
        {
            player.perfectDefense();
        }
        skillOn = false;
        attackOn = false;
        inputAllowed = true;
        moveAllowed = true;
        this.addMessage( '\n' + "---Select Action---" + '\n' );
        this.addMessage( "(1) Move" );
        this.addMessage( "(2) Attack" );
        this.addMessage( "(3) Skills" );
        this.addMessage( "(4) Next Turn" );

    }


    public boolean keyPressed( String description, Location loc )
    {
        // snipe
        if ( skillOn && description.equals( "1" ) )
        {
            if ( player.currentMP() < 5 )
            {
                this.addMessage( "Insufficent MP" );
                keyPressed( "" + ( level + 1 ), player.getLocation() );
            }
            else
            {
                player.snipe();
                this.addMessage( '\n' + "--Click on an enemy to target--" );
            }
        }
        // Whirlwind
        else if ( level >= 2 && skillOn && description.equals( "2" ) )
        {
            if ( player.currentMP() < 15 )
            {
                this.addMessage( "Insufficent MP" );
                keyPressed( "" + ( level + 1 ), player.getLocation() );
            }
            else if ( player.getGrid()
                .getNeighbors( player.getLocation() )
                .size() != 0 )
            {
                player.whirlwind();
                for ( Actor defender : player.getGrid()
                    .getNeighbors( player.getLocation() ) )
                {
                    this.attack( player, (Mobs)defender );
                }
                count++;
                takeStep();
            }
            else
            {
                this.addMessage( "No Enemies in Range" );
                keyPressed( "" + ( level + 1 ), player.getLocation() );
            }
        }
        // perfectDefense
        else if ( level >= 3 && skillOn && description.equals( "3" ) )
        {
            if ( player.currentMP() < 30 )
            {
                this.addMessage( "Insufficent MP" );
                keyPressed( "" + ( level + 1 ), player.getLocation() );
            }
            else
            {
                player.perfectDefense();
                this.attack( player, order.get( 1 ) );
                count++;
                takeStep();
            }
        }
        // back
        else if ( ( skillOn && description.equals( "" + ( level + 1 ) ) )
            || ( attackOn && description.equals( "1" ) ) )
        {
            skillOn = false;
            inputAllowed = true;
            this.addMessage( '\n' + "---Select Another Action---" + '\n' );
            if ( moveAllowed )
            {
                this.addMessage( "(1) Move" );
            }
            this.addMessage( "(2) Attack" );
            this.addMessage( "(3) Skills" );
            this.addMessage( "(4) Next Turn" );

        }
        // move
        else if ( moveAllowed && inputAllowed && description.equals( "1" ) )
        {
            moveAllowed = false;
            inputAllowed = false;
            this.addMessage( '\n' + "--Please select move location--" );
            player.highlightMoveLocations( Color.yellow );
        }

        // attack
        else if ( inputAllowed && description.equals( "2" ) )
        {
            inputAllowed = false;
            int i = 0;
            ArrayList<Actor> list = player.getGrid()
                .getNeighbors( player.getLocation() );
            while ( list.size() != 0 && i < list.size() )
            {
                Character a = (Character)list.get( i );
                if ( a.distance( player.getLocation(), a.getLocation() ) != 1 )
                {
                    list.remove( a );
                    i--;
                }
                i++;
            }
            if ( list.size() == 0 )
            {
                inputAllowed = true;
                this.addMessage( '\n' + "No avaliable enemies to attack." );
                this.addMessage( "---Select Another Action---" + '\n' );
                if ( moveAllowed )
                {
                    this.addMessage( "(1) Move" );
                }
                this.addMessage( "(2) Attack" );
                this.addMessage( "(3) Skills" );
                this.addMessage( "(4) Next Turn" );
            }
            else
            {
                attackOn = true;
                player.attack();
                this.addMessage( '\n' + "--Click on enemy in adjacent cardinal direction to attack--" );
                this.addMessage( "(1) Back" );
            }
        }

        // skills
        else if ( inputAllowed && description.equals( "3" ) )
        {
            skillOn = true;
            inputAllowed = false;
            this.addMessage( '\n' + "--Select an Ability--" );
            this.addMessage( "(1) Snipe - 5 MP" );
            if ( level >= 2 )
            {
                this.addMessage( "(2) Whirlwind - 15 MP" );
            }
            if ( level >= 3 )
            {
                this.addMessage( "(3) Perfect Defense - 30 MP" );
            }
            this.addMessage( "(" + ( level + 1 ) + ") Back" );
        }

        // endturn
        else if ( inputAllowed && description.equals( "4" ) )
        {
            count++;
            takeStep();
        }

        return true;
    }


    public boolean locationClicked( Location loc )
    {
        Grid<Actor> g = this.getGrid();
        if ( g.get( loc ) instanceof Character && !( attackOn || skillOn ) )
        {
            Character c = (Character)g.get( loc );
            this.addMessage( '\n' + c.getName() + '\n' + "HP: " + c.currentHP()
                + "/" + c.getHP() );
            this.addMessage( "" + "MP: " + c.currentMP() + "/" + c.getMP() );
            this.addMessage( "" + "ATK: " + c.getSTR() );
            this.addMessage( "" + "DEF: " + c.getDEF() );
            this.addMessage( "" + "SPD: " + c.getSPD() );
        }

        if ( g.get( loc ) instanceof TextCell )
        {
            inputAllowed = true;
            player.removeHighlights();
            player.moveTo( loc );
            this.addMessage( '\n' + "---Select Action---" + '\n' );
            this.addMessage( "(2) Attack" );
            this.addMessage( "(3) Skills" );
            this.addMessage( "(4) Next Turn" );
        }

        if ( g.get( loc ) instanceof Mobs && ( attackOn || skillOn ) )
        {
            this.attack( player, (Character)player.getGrid().get( loc ) );
            count++;
            takeStep();
            attackOn = false;
            skillOn = false;
        }

        return true;
    }

    // public static void main( String[] args )
    // {
    // Combat c = new Combat( 2 );
    // c.initializeCombat();
    // c.player.attack();
    // c.attack( c.player, c.enemiesToFight.get( 0 ) );
    // System.out.println( c.enemiesToFight.get( 0 ).currentHP() );
    // }

}